home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Internet Info 1994 March
/
Internet Info CD-ROM (Walnut Creek) (March 1994).iso
/
networking
/
ip
/
ka9q
/
src890906.arc
/
FTPCLI.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-08-19
|
19KB
|
783 lines
/* FTP client (interactive user) code */
#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "session.h"
#include "cmdparse.h"
#include "timer.h"
#include "proc.h"
#include "tty.h"
#include "socket.h"
#include "ftp.h"
#include "ftpcli.h"
#include "commands.h"
#include "netuser.h"
static int doascii __ARGS((int argc,char *argv[],void *p));
static int dobinary __ARGS((int argc,char *argv[],void *p));
static int doftpcd __ARGS((int argc,char *argv[],void *p));
static int doget __ARGS((int argc,char *argv[],void *p));
static int dolist __ARGS((int argc,char *argv[],void *p));
static int dols __ARGS((int argc,char *argv[],void *p));
static int domkdir __ARGS((int argc,char *argv[],void *p));
static int donothing __ARGS((int argc,char *argv[],void *p));
static int doput __ARGS((int argc,char *argv[],void *p));
static int doquit __ARGS((int argc,char *argv[],void *p));
static int dormdir __ARGS((int argc,char *argv[],void *p));
static int dotype __ARGS((int argc,char *argv[],void *p));
static struct mbuf *getline __ARGS((struct session *sp,char *prompt));
static int getresp __ARGS((int s,int mincode));
static int getsub __ARGS((struct ftpcli *ftp,char *command,char *remotename,
char *localname));
static void sendport __ARGS((int s,struct sockaddr_in *socket));
extern char Nospace[],Badhost[];
static char Notsess[] = "Not an FTP session!\n";
static struct cmds Ftpcmds[] = {
"", donothing, 0, 0, NULLCHAR,
"ascii", doascii, 0, 0, NULLCHAR,
"binary", dobinary, 0, 0, NULLCHAR,
"cd", doftpcd, 0, 2, "cd <directory>",
"dir", dolist, 0, 0, NULLCHAR,
"list", dolist, 0, 0, NULLCHAR,
"get", doget, 0, 2, "get remotefile <localfile>",
"ls", dols, 0, 0, NULLCHAR,
"mkdir", domkdir, 0, 2, "mkdir <directory>",
"nlst", dols, 0, 0, NULLCHAR,
"quit", doquit, 0, 0, NULLCHAR,
"rmdir", dormdir, 0, 2, "rmdir <directory>",
"put", doput, 0, 2, "put localfile <remotefile>",
"type", dotype, 0, 0, NULLCHAR,
NULLCHAR, NULLFP, 0, 0, NULLCHAR,
};
/* Handle top-level FTP command */
int
doftp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct session *sp;
struct ftpcli ftp;
struct sockaddr_in fsocket;
int resp;
struct mbuf *bp,*bpsav;
char *cp;
int control;
/* Allocate a session control block */
if((sp = newsession(argv[1],FTP)) == NULLSESSION){
printf("Too many sessions\n");
freeargs(argc,argv);
return 1;
}
memset((char *)&ftp,0,sizeof(ftp));
ftp.control = ftp.data = -1;
sp->cb.ftp = &ftp; /* Downward link */
ftp.session = sp; /* Upward link */
ftp.output = Curproc;
fsocket.sin_family = AF_INET;
if(argc < 3)
fsocket.sin_port = IPPORT_FTP;
else
fsocket.sin_port = atoi(argv[2]);
freeargs(argc,argv);
Current = sp;
Mode = CONV_MODE;
if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
printf(Badhost,sp->name);
freesession(sp);
return 1;
}
/* Open the control connection */
if((control = sp->s = ftp.control = socket(AF_INET,SOCK_STREAM,0)) == -1){
printf("Can't create socket\n");
freesession(sp);
return 1;
}
printf("Trying %s...\n",psocket((struct sockaddr *)&fsocket));
if(connect(control,(char *)&fsocket,sizeof(fsocket)) == -1)
goto quit;
printf("FTP session %u connected to %s\n",(unsigned)(sp-Sessions),
sp->name);
/* Wait for greeting from server */
resp = getresp(control,200);
/* Now process responses and commands */
for(;;){
switch(resp){
case -1:
goto quit;
case 220: /* Sign-on banner; prompt for and send USER command */
bp = qdata("USER ",5);
append(&bp,getline(sp,"Enter user name: "));
if(send_mbuf(control,bp,0,NULLCHAR,0) == -1)
goto quit;
resp = getresp(control,200);
break;
case 331: /* Password prompt; get password */
ttysetmode(TTY_EDIT); /* turn off echo */
bp = getline(sp,"Password: ");
printf("\r\n");
ttysetmode(TTY_EDIT|TTY_ECHO);
if(len_mbuf(bp) != 0 && bp->data[0] != '\r'){
bp = pushdown(bp,5);
strcpy(bp->data,"PASS ");
if(send_mbuf(control,bp,0,NULLCHAR,0) == -1)
goto quit;
resp = getresp(control,200);
break;
} else
free_p(bp); /* Null response, don't send */
default: /* Note fall-thru */
/* Test the control channel first */
if(sockstate(control) == NULLCHAR)
goto quit;
bp = getline(sp,"ftp> ");
/* Copy because cmdparse modifies the original */
bpsav = copy_p(bp,len_mbuf(bp));
if((resp = cmdparse(Ftpcmds,bp->data,sp)) != -1){
/* Valid command, free buffer and get another */
free_p(bpsav);
} else {
/* Not a local cmd, send to remote server */
if(send_mbuf(control,bpsav,0,NULLCHAR,0) == -1){
free_p(bp);
goto quit;
}
resp = getresp(control,200);
}
free_p(bp);
break;
}
}
quit: cp = sockerr(control);
printf("FTP session %u closed: %s\n",(unsigned)(sp - Sessions),
cp != NULLCHAR ? cp : "EOF");
if(ftp.fp != NULLFILE && ftp.fp != stdout)
fclose(ftp.fp);
if(ftp.data != -1)
close_s(ftp.data);
if(ftp.control != -1)
close_s(ftp.control);
if(ftp.session != NULLSESSION)
freesession(ftp.session);
return 0;
}
/* Handle null line to avoid trapping on first command in table */
static int
donothing(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return 0;
}
/* Close session */
static int
doquit(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
int control;
struct session *sp;
sp = (struct session *)p;
if(sp == NULLSESSION)
return -1;
ftp = sp->cb.ftp;
control = ftp->control;
usprintf(control,"QUIT\r\n");
getresp(control,200); /* Get the closing message */
getresp(control,200); /* Wait for the server to close */
return -1;
}
/* Translate 'cd' to 'cwd' for convenience */
static int
doftpcd(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
struct session *sp;
sp = (struct session *)p;
if(sp == NULLSESSION)
return -1;
ftp = sp->cb.ftp;
usprintf(ftp->control,"CWD %s\r\n",argv[1]);
return getresp(ftp->control,200);
}
/* Translate 'mkdir' to 'xmkd' for convenience */
static int
domkdir(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
struct session *sp;
sp = (struct session *)p;
if(sp == NULLSESSION)
return -1;
ftp = sp->cb.ftp;
usprintf(ftp->control,"XMKD %s\r\n",argv[1]);
return getresp(ftp->control,200);
}
/* Translate 'rmdir' to 'xrmd' for convenience */
static int
dormdir(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
struct session *sp;
sp = (struct session *)p;
if(sp == NULLSESSION)
return -1;
ftp = sp->cb.ftp;
usprintf(ftp->control,"XRMD %s\r\n",argv[1]);
return getresp(ftp->control,200);
}
static int
dobinary(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *args[2];
args[1] = "I";
return dotype(2,args,p);
}
static int
doascii(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *args[2];
args[1] = "A";
return dotype(2,args,p);
}
/* Handle "type" command from user */
static int
dotype(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
int control;
struct session *sp;
sp = (struct session *)p;
if(sp == NULLSESSION)
return -1;
ftp = sp->cb.ftp;
control = ftp->control;
if(argc < 2){
switch(ftp->type){
case IMAGE_TYPE:
printf("Image\n");
break;
case ASCII_TYPE:
printf("Ascii\n");
break;
case LOGICAL_TYPE:
printf("Logical bytesize %u\n",ftp->logbsize);
break;
}
return 0;
}
switch(*argv[1]){
case 'i':
case 'I':
case 'b':
case 'B':
ftp->typesent = ftp->type = IMAGE_TYPE;
usprintf(control,"TYPE I\r\n");
break;
case 'a':
case 'A':
ftp->typesent = ftp->type = ASCII_TYPE;
usprintf(control,"TYPE A\r\n");
break;
case 'L':
case 'l':
ftp->typesent = ftp->type = LOGICAL_TYPE;
ftp->logbsize = atoi(argv[2]);
usprintf(control,"TYPE L %s\r\n",argv[2]);
break;
default:
printf("Invalid type %s\n",argv[1]);
return 1;
}
return getresp(control,200);
}
/* Start receive transfer. Syntax: get <remote name> [<local name>] */
static int
doget(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *remotename,*localname;
register struct ftpcli *ftp;
struct session *sp;
sp = (struct session *)p;
if(sp == NULLSESSION)
return -1;
ftp = sp->cb.ftp;
if(ftp == NULLFTP){
printf(Notsess);
return 1;
}
remotename = argv[1];
if(argc < 3)
localname = remotename;
else
localname = argv[2];
return getsub(ftp,"RETR",remotename,localname);
}
/* List remote directory. Syntax: dir <remote files> [<local name>] */
static int
dolist(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *remotename,*localname;
register struct ftpcli *ftp;
struct session *sp;
sp = (struct session *)p;
if(sp == NULLSESSION)
return -1;
ftp = sp->cb.ftp;
if(ftp == NULLFTP){
printf(Notsess);
return 1;
}
remotename = argv[1];
if(argc > 2)
localname = argv[2];
else
localname = NULLCHAR;
return getsub(ftp,"LIST",remotename,localname);
}
/* Remote directory list, short form. Syntax: ls <remote files> [<local name>] */
static int
dols(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *remotename,*localname;
register struct ftpcli *ftp;
struct session *sp;
sp = (struct session *)p;
if(sp == NULLSESSION)
return -1;
ftp = sp->cb.ftp;
if(ftp == NULLFTP){
printf(Notsess);
return 1;
}
remotename = argv[1];
if(argc > 2)
localname = argv[2];
else
localname = NULLCHAR;
return getsub(ftp,"NLST",remotename,localname);
}
/* Common code to LIST/NLST/RETR */
static int
getsub(ftp,command,remotename,localname)
struct ftpcli *ftp;
char *command,*remotename,*localname;
{
unsigned long total;
FILE *fp;
int cnt,resp,i,control;
char *mode;
struct sockaddr_in lsocket;
int32 startclk,rate;
if(ftp == NULLFTP)
return -1;
control = ftp->control;
switch(ftp->type){
case IMAGE_TYPE:
case LOGICAL_TYPE:
mode = WRITE_BINARY;
break;
case ASCII_TYPE:
mode = WRITE_TEXT;
break;
}
/* Send TYPE message, if necessary */
if(strcmp(command,"LIST") == 0 || strcmp(command,"NLST") == 0){
if(ftp->typesent != ASCII_TYPE){
/* Directory listings are always in ASCII */
usprintf(control,"TYPE A\r\n");
ftp->typesent = ASCII_TYPE;
resp = getresp(control,200);
if(resp == -1 || resp > 299){
return 1;
}
}
} else if(ftp->typesent != ftp->type){
switch(ftp->type){
case ASCII_TYPE:
usprintf(control,"TYPE A\r\n");
break;
case IMAGE_TYPE:
usprintf(control,"TYPE I\r\n");
break;
case LOGICAL_TYPE:
usprintf(control,"TYPE L %d\r\n",ftp->logbsize);
break;
}
ftp->typesent = ftp->type;
resp = getresp(control,200);
if(resp == -1 || resp > 299){
return 1;
}
}
if(localname == NULLCHAR){
fp = stdout;
} else if((fp = fopen(localname,mode)) == NULLFILE){
printf("Can't write %s: %s\n",localname,sys_errlist[errno]);
return 1;
}
/* Open the data connection */
ftp->data = socket(AF_INET,SOCK_STREAM,0);
listen(ftp->data,0); /* Accept only one connection */
ftp->state = RECEIVING_STATE;
/* Send the PORT message and wait for ack */
i = SOCKSIZE;
getsockname(ftp->data,(char *)&lsocket,&i);
sendport(control,&lsocket);
resp = getresp(control,200);
if(resp == -1 || resp > 299){
/* Error, quit */
if(fp != stdout)
fclose(fp);
close_s(ftp->data);
ftp->data = -1;
ftp->state = COMMAND_STATE;
return 1;
}
/* Generate the command to start the transfer and wait for ack */
if(remotename != NULLCHAR)
usprintf(control,"%s %s\r\n",command,remotename);
else
usprintf(control,"%s\r\n",command);
/* Get the intermediate "150" response */
resp = getresp(control,100);
if(resp == -1 || resp >= 400){
/* Error, quit */
if(fp != stdout)
fclose(fp);
close_s(ftp->data);
ftp->data = -1;
ftp->state = COMMAND_STATE;
return 1;
}
/* Wait for the server to open the data connection */
cnt = 0;
ftp->data = accept(ftp->data,NULLCHAR,&cnt);
startclk = Clock;
total = recvfile(fp,ftp->data,ftp->type);
/* Immediately close the data connection; some servers (e.g., TOPS-10)
* wait for the data connection to close completely before returning
* the completion message on the control channel
*/
close_s(ftp->data);
ftp->data = -1;
#ifdef CPM
if(ftp->type == ASCII_TYPE)
fputc(CTLZ,fp);
#endif
if(fp != stdout)
fclose(fp);
startclk = Clock - startclk;
if(startclk != 0)
rate = total/startclk;
else
rate = 0;
if(total != -1)
printf("Get complete: %lu bytes in %lu sec (%lu/sec)\n",
total,(startclk*MSPTICK)/1000,(rate*1000)/MSPTICK);
else
printf("Error or abort during data transfer\n");
getresp(control,200);
ftp->state = COMMAND_STATE;
return 0;
}
/* Send a file. Syntax: put <local name> [<remote name>] */
static int
doput(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *remotename,*localname,*mode;
register struct ftpcli *ftp;
int i,resp,control;
unsigned long total;
FILE *fp;
struct sockaddr_in lsocket;
int32 startclk,rate;
struct session *sp;
sp = (struct session *)p;
if(sp == NULLSESSION)
return -1;
ftp = sp->cb.ftp;
control = ftp->control;
if(ftp == NULLFTP){
printf(Notsess);
return 1;
}
localname = argv[1];
if(argc < 3)
remotename = localname;
else
remotename = argv[2];
if(ftp->type == IMAGE_TYPE)
mode = READ_BINARY;
else
mode = READ_TEXT;
/* Send TYPE message, if necessary */
if(ftp->typesent != ftp->type){
switch(ftp->type){
case ASCII_TYPE:
usprintf(control,"TYPE A\r\n");
break;
case IMAGE_TYPE:
usprintf(control,"TYPE I\r\n");
break;
case LOGICAL_TYPE:
usprintf(control,"TYPE L %d\r\n",ftp->logbsize);
break;
}
ftp->typesent = ftp->type;
resp = getresp(control,200);
if(resp == -1 || resp > 299){
return 1;
}
}
if((fp = fopen(localname,mode)) == NULLFILE){
printf("Can't read %s: %s\n",localname,sys_errlist[errno]);
return 1;
}
/* Open the data connection */
ftp->data = socket(AF_INET,SOCK_STREAM,0);
listen(ftp->data,0);
ftp->state = SENDING_STATE;
/* Send the PORT message and wait for ack */
i = SOCKSIZE;
getsockname(ftp->data,(char *)&lsocket,&i);
sendport(control,&lsocket);
resp = getresp(control,200);
if(resp == -1 || resp > 299){
/* Error, quit */
fclose(fp);
close_s(ftp->data);
ftp->data = -1;
ftp->state = COMMAND_STATE;
return 1;
}
/* Generate the command to start the transfer and wait for ack */
usprintf(control,"STOR %s\r\n",remotename);
resp = getresp(control,100);
if(resp == -1 || resp >= 400){
/* Error, quit */
fclose(fp);
close_s(ftp->data);
ftp->data = -1;
ftp->state = COMMAND_STATE;
return 1;
}
/* Wait for the data connection to open. Otherwise the first
* block of data would go out with the SYN, and this may confuse
* some other TCPs
*/
accept(ftp->data,NULLCHAR,(int *)NULL);
startclk = Clock;
total = sendfile(fp,ftp->data,ftp->type);
close_s(ftp->data);
ftp->data = -1;
fclose(fp);
startclk = Clock - startclk;
if(startclk != 0)
rate = total/startclk;
else
rate = 0;
if(total != -1)
printf("Put complete: %lu bytes in %lu sec (%lu/sec)\n",
total,(startclk*MSPTICK)/1000,(rate*1000)/MSPTICK);
else
printf("Error or abort during data transfer\n");
getresp(control,200);
ftp->state = COMMAND_STATE;
return 1;
}
/* Abort a GET or PUT operation in progress. Note: this will leave
* the partial file on the local or remote system
*/
int
doabort(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
struct session *sp;
sp = (struct session *)p;
if(sp == NULLSESSION)
return -1;
/* Default is the current session, but it can be overridden with
* an argument.
*/
if(argc > 1)
sp = sessptr(argv[1]);
if(sp == NULLSESSION || sp->type != FTP){
printf("Not an active FTP session\n");
return 1;
}
ftp = sp->cb.ftp;
switch(ftp->state){
case COMMAND_STATE:
printf("No active transfer\n");
return 1;
case SENDING_STATE:
/* Send a premature EOF.
* Unfortunately we can't just reset the connection
* since the remote side might end up waiting forever
* for us to send something.
*/
shutdown(ftp->data,1);
break;
case RECEIVING_STATE:
/* Just blow away the receive socket */
shutdown(ftp->data,2);
break;
}
return 0;
}
/* send PORT message */
static void
sendport(s,socket)
int s;
struct sockaddr_in *socket;
{
struct mbuf *bp;
/* Compose and send PORT a,a,a,a,p,p message */
if((bp = alloc_mbuf(35)) == NULLBUF){ /* 5 more than worst case */
printf(Nospace);
return;
}
/* I know, this looks gross, but it works! */
sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
hibyte(hiword(socket->sin_addr.s_addr)),
lobyte(hiword(socket->sin_addr.s_addr)),
hibyte(loword(socket->sin_addr.s_addr)),
lobyte(loword(socket->sin_addr.s_addr)),
hibyte(socket->sin_port),
lobyte(socket->sin_port));
bp->cnt = strlen(bp->data);
send_mbuf(s,bp,0,NULLCHAR,0);
}
/* Wait for, read and display response from FTP server. Return the result code.
*/
int
getresp(s,mincode)
int s;
int mincode; /* Keep reading until at least this code comes back */
{
register char *line;
int rval;
line = malloc(256);
for(;;){
/* Get line */
if(recvline(s,line,256) == -1){
rval = -1;
break;
}
rip(line); /* Remove cr/lf */
printf("%s\n",line); /* Display to user */
/* Messages with dashes are continued */
if(line[3] != '-' && (rval = atoi(line)) >= mincode)
break;
}
free(line);
return rval;
}
/* Issue a prompt and read a line from the user */
struct mbuf *
getline(sp,prompt)
struct session *sp;
char *prompt;
{
/* If there's something already there, just grab it */
if(sp->input != NULLBUF)
return dequeue(&sp->input);
/* Wait until we're the current session, issue prompt, and get line */
while(Current != sp || Mode != CONV_MODE)
pwait(sp);
printf("%s",prompt);
while(sp->input == NULLBUF)
pwait(&sp->input);
return dequeue(&sp->input);
}